package com.xwaf.platform.system.provider.serviceimpl.gateway;

import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

import javax.annotation.Resource;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.plugins.Page;
import com.xwaf.platform.common.api.constants.RedisKeys;
import com.xwaf.platform.common.api.entity.TokenInfo;
import com.xwaf.platform.common.redis.util.RedisUtil;
import com.xwaf.platform.common.service.service.CrudService;
import com.xwaf.platform.system.api.entity.DataBase;
import com.xwaf.platform.system.api.entity.auth.User;
import com.xwaf.platform.system.api.entity.gateway.Token;
import com.xwaf.platform.system.api.request.auth.TokenVerifyRequest;
import com.xwaf.platform.system.api.service.gateway.TokenService;
import com.xwaf.platform.system.provider.dao.gateway.TokenDao;

@Transactional(readOnly = true)
@Service("tokenService")
public class TokenServiceImpl extends CrudService<TokenDao, Token> implements TokenService {

	private static final Logger log = LoggerFactory.getLogger(CurrentLimitServiceImpl.class);

	private long tokenExpire = 720; // token超时时长，单位为分钟

	@Resource
	private RedisTemplate<String, TokenInfo> redisTemplate;// old

	@Autowired
	private StringRedisTemplate stringRedisTemplate;// old

	@Autowired
	private RedisUtil redisUtil;// new

	@Autowired
	private TokenDao tokenDao;

	@Override
	public Page<Token> selPageList(Page<Token> page, Token token) {
		List<Token> list = tokenDao.selPageList(page, token);
		page.setRecords(list);
		return page;
	}

	@Override
	public TokenInfo verifyToken(TokenVerifyRequest tokenVerifyReq) throws Exception {
		TokenInfo tokenInfo = null;
		try {
			if (redisUtil.exists(RedisKeys.TOKEN_INFO + tokenVerifyReq.getToken())) {
				log.info("认证:从缓存种获取用户信息");
				tokenInfo = (TokenInfo) redisUtil.get(RedisKeys.TOKEN_INFO + tokenVerifyReq.getToken());
				return tokenInfo;
			}
		} catch (Exception e) {
			e.printStackTrace();
			log.error("认证:从缓存中检测token失败");
		}
		Token token = new Token();
		token.setToken(tokenVerifyReq.getToken());
		token = tokenDao.selectOne(token);
		// 获取token
		if (token != null && token.getExpireAt().getTime() >= System.currentTimeMillis()) {
			tokenInfo = new TokenInfo();
			BeanUtils.copyProperties(tokenInfo, token);
		}
		return tokenInfo;
	}

	@Override
	@Transactional(readOnly = false, isolation = Isolation.REPEATABLE_READ, propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
	public TokenInfo applyToken(User user, String deviceType, String ip) throws Exception {
		long time = tokenExpire;
		long loginTime = System.currentTimeMillis();
		final TokenInfo tokenInfo = new TokenInfo();
		tokenInfo.setDeviceType(deviceType);
		tokenInfo.setLoginTime(new Date(loginTime));
		tokenInfo.setExpireAt(new Date(loginTime + time * 60 * 60 * 1000));
		tokenInfo.setExpiryInMilli(time * 60 * 60 * 1000);
		tokenInfo.setUserId(user.getId());
		tokenInfo.setToken(DataBase.getDataBaseId(DataBase.TN));
		tokenInfo.setLoginIp(ip);
		tokenInfo.setEmail(user.getEmail());
		tokenInfo.setMobile(user.getMobile());
		Token token = new Token();
		BeanUtils.copyProperties(token, tokenInfo);
		token.setId(DataBase.getDataBaseId(DataBase.TN));
		saveTokenToRedis(tokenInfo);
		tokenDao.insert(token);
		return tokenInfo;
	}

	// 将token信息保存到Redis
	private void saveTokenToRedis(TokenInfo tokenInfo) {
		try {
			redisUtil.set(RedisKeys.USER_TOKEN + tokenInfo.getUserId(), tokenInfo.getToken(),
					tokenInfo.getExpiryInMilli(), TimeUnit.MILLISECONDS);
			redisUtil.set(RedisKeys.TOKEN_INFO + tokenInfo.getToken(), tokenInfo, tokenInfo.getExpiryInMilli(),
					TimeUnit.MILLISECONDS);
		} catch (Exception e) {
			log.error("保存token到redis失败,{}", tokenInfo, e);
		}
	}

	@Override
	public TokenInfo getLastToken(String deviceType, Long uid) throws Exception {
		TokenInfo tokenInfo = null;
		try {
			String token = (String) redisUtil.get(RedisKeys.USER_TOKEN + uid);
			if (!StringUtils.isEmpty(token)) {
				tokenInfo = (TokenInfo) redisUtil.get(RedisKeys.TOKEN_INFO + token);
			}
		} catch (Exception e) {
			log.error("从redis中获取token失败,{}", uid, e);
		}
		if (tokenInfo == null) {
			Token tk = tokenDao.getLastTokenInfoByUid(uid);
			if (tk != null) {
				tokenInfo = new TokenInfo();
				BeanUtils.copyProperties(tk, tokenInfo);
			}
		}
		return tokenInfo;
	}

	@Override
	public void clearToken(String deviceType, String uid) {
		if (uid == null) {
			return;
		}
		if (redisUtil.exists(RedisKeys.USER_TOKEN + uid)) {
			String token = (String) redisUtil.get(RedisKeys.USER_TOKEN + uid);
			redisUtil.remove(RedisKeys.USER_TOKEN + uid);
			redisUtil.remove(RedisKeys.TOKEN_INFO + token);
		}
	}

}
